Skip to content

Fix OH refundable EITC reform zero-refund and lost-credit bugs#8657

Open
PavelMakarchuk wants to merge 3 commits into
mainfrom
fix-oh-refundable-eitc-reform-zero-refund
Open

Fix OH refundable EITC reform zero-refund and lost-credit bugs#8657
PavelMakarchuk wants to merge 3 commits into
mainfrom
fix-oh-refundable-eitc-reform-zero-refund

Conversation

@PavelMakarchuk

@PavelMakarchuk PavelMakarchuk commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Fixes #8656

Problem

The bundled Ohio refundable-EITC contrib reform (gov.contrib.states.oh.child_poverty_impact_dashboard.eitc.in_effect) ran without crashing but was functionally wrong on two fronts — same shape as the MO and UT bugs fixed in #8642 and #8645, but no add() string crash:

  1. Zero refund at zero liability. oh_refundable_eitc.formula returned tax_unit("oh_eitc", period) — the applied nonrefundable-capped EITC. A zero-liability filer's oh_eitc is 0, so the reform's oh_refundable_eitc was also 0 — no refund paid in exactly the case refundability is meant to address.

  2. Other Ohio non-refundable credits discarded. oh_non_refundable_credits.formula returned just tax_unit("oh_non_refundable_eitc", period) (= 0 under the reform), silently zeroing out every other non-refundable credit walked by the baseline ordered list (oh_cdcc, oh_senior_citizen_credit, oh_retirement_credit, oh_non_public_school_credits, oh_exemption_credit, oh_joint_filing_credit, plus oh_adoption_credit for pre-2023 years). An OH filer claiming any of those lost them all whenever the reform was active.

Fixes in oh_refundable_eitc_reform.py

oh_refundable_eitc now reads oh_eitc_potential (uncapped at liability; OH does not apply a separate cap layer above the potential, so this is the full refundable amount). Mirrors the MO mo_wftcmo_wftc_potential change in #8642 and the UT ut_eitcut_eitc_potential change in #8645.

oh_non_refundable_credits now walks the same ordered list the baseline uses (gov.states.oh.tax.income.credits.non_refundable) via ordered_capped_state_non_refundable_credits, with oh_eitc filtered out of the list since the reform pays it as refundable. Identical pattern to the UT fix.

Verification

  • All four updated contrib tests pass (drive from federal eitc and oh_income_tax_before_non_refundable_credits rather than injecting capped oh_eitc, in line with the new uncapped behaviour).
  • New tests/policy/reform/oh_refundable_eitc.yaml adds four regression tests: full refundable payout at zero liability (asserting oh_income_tax goes negative, i.e. the refund is actually paid); confirmation that oh_joint_filing_credit still applies under the reform (proves the other credits are no longer discarded); no double-counting when liability partially absorbs other credits; and the filtered ordered walk capping multiple non-EITC credits at remaining liability.
  • All 234 Ohio baseline tests still pass.
  • The test_non_refundable_credit_downstream_consumers.py invariant still passes (the reform's reference to oh_eitc in the filter is unchanged).

Why this slipped through

Same test-shielding pattern as MO/UT — see the OH stanza of the bug-pattern analysis in the parent issue. The existing contrib tests pinned oh_eitc: 500 as input and only checked oh_refundable_eitc / oh_non_refundable_eitc / oh_non_refundable_credits with no other OH credits present, which dodged both broken code paths.

🤖 Generated with Claude Code

PavelMakarchuk and others added 2 commits June 16, 2026 21:36
Mirrors the UT fix in #8645 for Ohio's analogous bundled reform.

- oh_refundable_eitc: pay the uncapped potential credit
  (oh_eitc_potential) instead of the tax-liability-capped oh_eitc so the
  reform delivers a refund to zero-liability filers (the case
  refundability is meant to help).
- oh_non_refundable_credits: replace the formula that returned only
  oh_non_refundable_eitc (= 0) — which silently discarded Ohio's other
  six non-refundable credits — with ordered_capped_state_non_refundable_credits
  on the same ordered list, filtering out oh_eitc since it is paid as
  refundable here.

Updates the contrib tests to drive from federal eitc and pin
oh_income_tax_before_non_refundable_credits, mirroring the UT pattern,
and adds a new regression test verifying that other OH non-refundable
credits (e.g. oh_joint_filing_credit) still apply under the reform.

Fixes #8656

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add 2023/2024/2025 OH Schedule of Credits references on
  oh_refundable_credits and oh_non_refundable_credits to match the
  baseline non_refundable.yaml parameter.
- Note in the docstring that this reform departs from ORC § 5747.71
  (which makes the credit nonrefundable) — it is a contrib/what-if
  module, not a baseline change.
- Add two reform tests for previously-untested edges of the ordered
  cap walk: partial-absorption (refundable EITC paid in full while
  other credits consume the smaller remaining liability) and a
  multi-credit binding case (CDCC + exemption + joint-filing
  competing for liability, total capped at remaining liability).
- Tighten test comment ("OH EITC rate is 30%, constant since 2020"
  instead of "2024 OH EITC rate") and changelog wording ("zeroed out"
  instead of "discarded").

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@PavelMakarchuk PavelMakarchuk requested a review from hua7450 June 17, 2026 02:17
- Add an oh_income_tax end-to-end refund assertion to the zero-liability
  reform test, confirming the refund actually pays out (not just lands in
  the refundable bucket).
- Document the HB 62 (eff. 2019-07-03) cap repeal in the reform docstring,
  explaining why reading oh_eitc_potential is the full refundable amount
  for the modeled era (ORC 5747.71).
- Correct the "six other credits" wording in the code comment and test
  header to "every other entry in the ordered list" — the 2021-2022 list
  also carries oh_adoption_credit (repealed 2023). The formula was already
  correct since it filters the live period-specific list.
- Swap the non-OH negative test from CA to TX.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hua7450

hua7450 commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Reviewed locally with a multi-validator pass — regulatory, references, code patterns, test coverage, plus a deep dataflow trace run live in a Simulation. The fix is correct. I pushed a small follow-up commit (3ba79b6) with documentation/test polish (no behavior change); details below.

Correctness verified

  • Reading oh_eitc_potential yields the full refundable amount for the modeled era (2020+): Ohio's pre-2019 "50% of tax above $20,000 OH taxable income" cap was repealed by HB 62 (eff. 2019-07-03), leaving only the ordinary liability cap on the nonrefundable oh_eitc — exactly what refundability lifts. No intermediate cap is silently skipped.
  • No double-counting — EITC flows through the refundable path only (oh_non_refundable_eitc → 0 and filtered out of the ordered nonrefundable walk).
  • The ordered-cap rewrite preserves every other nonrefundable credit, filtering exactly oh_eitc.
  • Tests are non-vacuous — each assertion fails on the pre-fix code and passes after.

Follow-up commit (polish only, 3ba79b6)

  • Added an end-to-end assertion (oh_income_tax: -1_500) to the zero-liability reform test, confirming the refund actually pays out rather than just landing in the refundable bucket.
  • Documented the HB 62 cap repeal in the reform docstring, explaining why reading oh_eitc_potential is safe (ORC §5747.71).
  • Corrected the "six other credits" wording in the code comment, test header, and PR description — the 2021–2022 ordered list also carries oh_adoption_credit (repealed 2023), so it's "every other entry in the ordered list." The formula was already correct (it filters the live period-specific list).
  • Swapped the non-OH negative test from CA to TX.

Reform tests (4) and contrib tests (4) pass locally; leaving CI to confirm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OH refundable EITC reform (create_oh_refundable_eitc) pays zero refund and discards other non-refundable credits

2 participants